/*
 * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: Apache-2.0
 */
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "avifile.h"

static const char *TAG = "avifile"; // 日志标签

// 宏定义：创建四字符码（FourCC）
#define MAKE_FOURCC(a, b, c, d) ((uint32_t)(d) << 24 | (uint32_t)(c) << 16 | (uint32_t)(b) << 8 | (uint32_t)(a))

// 字节序反转函数（小端转大端）
static uint32_t _REV(uint32_t value)
{
    return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 |
           (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24;
}

/**
 * @brief 在缓冲区中搜索指定的FourCC值
 *
 * @param fourcc 要搜索的FourCC值
 * @param buffer 缓冲区指针
 * @param length 缓冲区长度
 * @return int 找到的位置偏移量，未找到返回-1
 */
static int search_fourcc(uint32_t fourcc, const uint8_t *buffer, uint32_t length)
{
    uint32_t i, j;
    uint32_t *pdata;
    j = length - 4; // 有效搜索范围

    // 遍历缓冲区
    for (i = 0; i < j; i++) {
        pdata = (uint32_t *)(buffer + i);
        // 比较FourCC值
        if (fourcc == *pdata) {
            return i; // 返回匹配位置
        }
    }
    return -1; // 未找到
}

/**
 * @brief 解析AVI流列表（strl）
 *
 * @param AVI_file AVI文件结构指针
 * @param buffer 缓冲区指针
 * @param length 缓冲区长度
 * @param list_length 返回列表总长度
 * @return int 错误码：0=成功，负数=失败
 */
static int strl_parser(avi_typedef *AVI_file, const uint8_t *buffer, uint32_t length, uint32_t *list_length)
{
    // TODO: 处理列表不完整的情况
    const uint8_t *pdata = buffer;

    // 解析流列表头
    AVI_LIST_HEAD *strl = (AVI_LIST_HEAD *)pdata;
    // 检查列表标识
    if (strl->List != LIST_ID || strl->FourCC != STRL_ID) {
        return -1; // 无效列表头
    }
    pdata += sizeof(AVI_LIST_HEAD);
    *list_length = strl->size + 8; // 列表总大小 = 数据大小 + 头大小

    // 解析流头信息（strh）
    AVI_STRH_CHUNK *strh = (AVI_STRH_CHUNK *)pdata;
    // 检查流头标识和大小
    if (strh->FourCC != STRH_ID || strh->size + 8 != sizeof(AVI_STRH_CHUNK)) {
        return -5; // 无效流头
    }

#ifdef CONFIG_AVI_PLAYER_DEBUG_INFO
    // 调试信息输出
    printf("-----strh info------\r\n");
    // ... [省略调试输出代码] ...
#endif

    pdata += sizeof(AVI_STRH_CHUNK);

    // 视频流处理
    if (VIDS_ID == strh->fourcc_type) {
        ESP_LOGI(TAG, "Found video stream");
        // 检查视频编码格式
        if (MJPG_ID == strh->fourcc_codec) {
            AVI_file->vids_format = FORMAT_MJEPG; // MJPEG格式
        } else if (H264_ID == strh->fourcc_codec) {
            AVI_file->vids_format = FORMAT_H264; // H264格式
        } else {
            ESP_LOGE(TAG, "Unsupported MJPEG/H264 format 0x%" PRIx32 "", strh->fourcc_codec);
            return -1; // 不支持的编码格式
        }

        // 解析视频流格式信息（strf）
        AVI_VIDS_STRF_CHUNK *strf = (AVI_VIDS_STRF_CHUNK *)pdata;
        if (strf->FourCC != STRF_ID || strf->size + 8 != sizeof(AVI_VIDS_STRF_CHUNK)) {
            return -5; // 无效视频流格式
        }

#ifdef CONFIG_AVI_PLAYER_DEBUG_INFO
        // 调试信息输出
        printf("-----video strf info------\r\n");
        // ... [省略调试输出代码] ...
#endif

        // 提取视频参数
        AVI_file->vids_fps = strh->rate / strh->scale; // 帧率 = 速率/时间刻度
        AVI_file->vids_width = strf->width;            // 视频宽度
        AVI_file->vids_height = strf->height;          // 视频高度
        pdata += sizeof(AVI_VIDS_STRF_CHUNK);
    }
    // 音频流处理
    else if (AUDS_ID == strh->fourcc_type) {
        ESP_LOGI(TAG, "Found video stream");
        // 解析音频流格式信息（strf）
        AVI_AUDS_STRF_CHUNK *strf = (AVI_AUDS_STRF_CHUNK *)pdata;
        // 检查音频流格式（允许两种大小格式）
        if (strf->FourCC != STRF_ID ||
            (strf->size + 8 != sizeof(AVI_AUDS_STRF_CHUNK) &&
             strf->size + 10 != sizeof(AVI_AUDS_STRF_CHUNK))) {
            ESP_LOGE(TAG, "FourCC=0x%" PRIx32 "|%" PRIx32 ", size=%" PRIu32 "|%d",
                     strf->FourCC, STRF_ID, strf->size, sizeof(AVI_AUDS_STRF_CHUNK));
            return -5; // 无效音频流格式
        }

#ifdef CONFIG_AVI_PLAYER_DEBUG_INFO
        // 调试信息输出
        printf("-----audio strf info------\r\n");
        // ... [省略调试输出代码] ...
#endif

        // 提取音频参数
        AVI_file->auds_channels = strf->channels;           // 音频通道数
        AVI_file->auds_sample_rate = strf->samples_per_sec; // 采样率
        AVI_file->auds_bits = strf->bits_per_sample;        // 采样位数
        pdata += sizeof(AVI_AUDS_STRF_CHUNK);
    }
    // 其他类型流处理
    else {
        ESP_LOGW(TAG, "Unsupported stream type 0x%" PRIu32 "", strh->fourcc_type);
    }
    return 0; // 成功
}

/**
 * @brief AVI文件解析主函数
 *
 * @param AVI_file AVI文件结构指针
 * @param buffer 文件数据缓冲区
 * @param length 缓冲区长度
 * @return int 错误码：0=成功，负数=失败
 */
int avi_parser(avi_typedef *AVI_file, const uint8_t *buffer, uint32_t length)
{
    const uint8_t *pdata = buffer;

    // 解析RIFF头
    AVI_LIST_HEAD *riff = (AVI_LIST_HEAD *)pdata;
    // 检查RIFF标识
    if (riff->List != RIFF_ID || riff->FourCC != AVI_ID) {
        return -1; // 无效RIFF头
    }
    AVI_file->RIFFchunksize = riff->size; // 保存RIFF块大小
    pdata += sizeof(AVI_LIST_HEAD);

    // 解析LIST头（hdrl）
    AVI_LIST_HEAD *list = (AVI_LIST_HEAD *)pdata;
    if (list->List != LIST_ID || list->FourCC != HDRL_ID) {
        return -3; // 无效LIST头
    }
    AVI_file->LISTchunksize = list->size; // 保存LIST块大小
    pdata += sizeof(AVI_LIST_HEAD);

    // 解析AVI主信息头（avih）
    AVI_AVIH_CHUNK *avih = (AVI_AVIH_CHUNK *)pdata;
    if (avih->FourCC != AVIH_ID || avih->size + 8 != sizeof(AVI_AVIH_CHUNK)) {
        return -5; // 无效AVI主信息头
    }
    AVI_file->avihsize = avih->size; // 保存avih块大小

#ifdef CONFIG_AVI_PLAYER_DEBUG_INFO
    // 调试信息输出
    printf("-----avih info------\r\n");
    // ... [省略调试输出代码] ...
#endif

    pdata += sizeof(AVI_AVIH_CHUNK);

    // 处理所有流信息
    for (size_t i = 0; i < avih->streams; i++) {
        uint32_t strl_size = 0;
        // 解析每个流的信息
        int ret = strl_parser(AVI_file, pdata, length - (pdata - buffer), &strl_size);
        if (0 > ret) {
            ESP_LOGE(TAG, "Stream %dAnalysis failed", i);
            break;
            // TODO: 处理错误（例如搜索下一个strl）
        }
        pdata += strl_size; // 移动到下一个流
    }

    // 搜索'movi'列表位置
    int movi_offset = search_fourcc(MAKE_FOURCC('m', 'o', 'v', 'i'), pdata, length - (pdata - buffer));
    if (0 > movi_offset) {
        ESP_LOGE(TAG, "not found \"movi\" list");
        return -7; // 找不到movi列表
    }

    // 计算movi数据区起始位置
    AVI_file->movi_start = movi_offset + 4 + pdata - buffer;

    // 定位到movi列表头
    pdata += movi_offset - 8;
    AVI_LIST_HEAD *movi = (AVI_LIST_HEAD *)pdata;
    if (movi->List != LIST_ID || movi->FourCC != MOVI_ID) {
        return -8; // 无效movi列表头
    }

    // 保存movi数据区大小
    AVI_file->movi_size = movi->size;
    pdata += sizeof(AVI_LIST_HEAD);

    ESP_LOGI(TAG, "movi pos:%" PRIu32 ", size:%" PRIu32 "", AVI_file->movi_start, AVI_file->movi_size);
    return 0; // 解析成功
}